package common

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"time"
)

const (
	// AESKey 随机种子
	AESKey = "Qa1kjtR^@@c2#9&1"
)

// Md5Encode ...
func Md5Encode(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

// DataWithExpire 有时限的加解密数据
type DataWithExpire struct {
	Data   string
	Expire int64
}

// AesEncryptOverdue ...
func AesEncryptOverdue(origData string, overdueTime int64) string {
	ot := time.Now().Unix() + overdueTime
	var strArr DataWithExpire
	strArr.Data = origData
	strArr.Expire = ot
	jsonStr, _ := json.Marshal(strArr)
	return AesHandlerEncrypt(string(jsonStr), "")
}

// AesDecryptOverdue ...
func AesDecryptOverdue(input string) string {
	decodeData := AesHandlerDecrypt(input, "")
	var d DataWithExpire
	err := json.Unmarshal([]byte(decodeData), &d)
	if err != nil {
		return ""
	}
	if d.Expire > time.Now().Unix() {
		return d.Data
	}
	return ""
}

// AesHandlerEncrypt ...
func AesHandlerEncrypt(origData string, key string) string {
	if origData == "" {
		return ""
	}
	if key == "" {
		key = AESKey
	}

	keyByte := []byte(key)
	result, err := AesEncrypt([]byte(origData), keyByte)
	if err != nil {
		return ""
	}
	ret := base64.RawURLEncoding.EncodeToString(result)
	return ret
}

// AesEncrypt ...
func AesEncrypt(origData, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	blockSize := block.BlockSize()
	origData = PKCS5Padding(origData, blockSize)
	// origData = ZeroPadding(origData, block.BlockSize())
	blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
	crypted := make([]byte, len(origData))
	// 根据CryptBlocks方法的说明，如下方式初始化crypted也可以
	blockMode.CryptBlocks(crypted, origData)
	return crypted, nil
}

// PKCS5Padding ...
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

// AesHandlerDecrypt ...
func AesHandlerDecrypt(origData string, key string) string {
	if origData == "" {
		return ""
	}
	datastr, err := base64.RawURLEncoding.DecodeString(origData)
	if err != nil {
		return ""
	}
	if key == "" {
		key = AESKey
	}
	keybyte := []byte(key)
	decodeData, err := AesDecrypt(datastr, keybyte)
	if err != nil {
		return ""
	}
	return string(decodeData)
}

// AesDecrypt ...
func AesDecrypt(crypted, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockSize := block.BlockSize()
	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
	origData := make([]byte, len(crypted))
	if len(crypted)%blockSize != 0 {
		return nil, errors.New("err data")
	}
	blockMode.CryptBlocks(origData, crypted)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

// PKCS5UnPadding ...
func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	// 去掉最后一个字节 unpadding 次
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
